/** * This file Copyright (c) 2011-2015 Magnolia International * Ltd. (http://www.magnolia-cms.com). All rights reserved. * * * This file is dual-licensed under both the Magnolia * Network Agreement and the GNU General Public License. * You may elect to use one or the other of these licenses. * * This file is distributed in the hope that it will be * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT. * Redistribution, except as permitted by whichever of the GPL * or MNA you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or * modify this file under the terms of the GNU General * Public License, Version 3, as published by the Free Software * Foundation. You should have received a copy of the GNU * General Public License, Version 3 along with this program; * if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * 2. For the Magnolia Network Agreement (MNA), this file * and the accompanying materials are made available under the * terms of the MNA which accompanies this distribution, and * is available at http://www.magnolia-cms.com/mna.html * * Any modifications to this file must keep this entire header * intact. * */ package com.puglieseweb.app.web.config; import info.magnolia.context.MgnlContext; import info.magnolia.link.LinkException; import info.magnolia.link.LinkUtil; import info.magnolia.module.blossom.view.UuidRedirectViewResolver; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.web.util.UrlPathHelper; import org.springframework.webflow.context.servlet.DefaultFlowUrlHandler; import org.springframework.webflow.context.servlet.ServletExternalContext; import org.springframework.webflow.core.FlowException; import org.springframework.webflow.core.collection.AttributeMap; import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.execution.FlowExecutionOutcome; import org.springframework.webflow.mvc.servlet.AbstractFlowHandler; import org.springframework.webflow.mvc.servlet.FlowController; import org.springframework.webflow.mvc.servlet.FlowHandlerAdapter; /** * Abstract base class for controllers serving a single WebFlow. Subclasses must set the FlowExecutor and when using * @MVC should override <code>handleRequest</code> and add @RequestMapping to it. * <p/> * Default behaviour: * <ul> * <li>when a NoSuchFlowExecutionException is thrown a redirect will be made to the original request uri</li> * <li>on other exceptions the exceptions bubbles out to the DispatcherServlet exception handling</li> * <li>when the flow completes and no view is set a redirect will be made to the original request uri</li> * </ul> * * @see FlowController * @see FlowHandlerAdapter * @see org.springframework.webflow.mvc.servlet.FlowHandler */ public abstract class AbstractSingleFlowController extends FlowController { /** * Copied from DefaultFlowUrlHandler since it's private there. */ private static final String DEFAULT_FLOW_EXECUTION_KEY_PARAMETER = "execution"; private String flowExecutionKeyParameter = DEFAULT_FLOW_EXECUTION_KEY_PARAMETER; private UrlPathHelper urlPathHelper = new UrlPathHelper(); private CustomFlowUrlHandler flowUrlHandler; private String flowId; protected AbstractSingleFlowController(String flowId) { this.flowId = flowId; this.flowUrlHandler = new CustomFlowUrlHandler(); CustomFlowHandlerAdapter flowHandlerAdapter = new CustomFlowHandlerAdapter(); flowHandlerAdapter.setFlowUrlHandler(flowUrlHandler); super.setFlowHandlerAdapter(flowHandlerAdapter); super.registerFlowHandler(new CustomFlowHandler()); } /** * Extension point for customizing the default behaviour in FlowUrlHandler. * * @see org.springframework.webflow.context.servlet.FlowUrlHandler#getFlowId(javax.servlet.http.HttpServletRequest) */ public String getFlowId(HttpServletRequest request) { return flowId; } /** * Extension point for customizing the default behaviour in FlowUrlHandler. * * @see org.springframework.webflow.context.servlet.FlowUrlHandler#getFlowExecutionKey(javax.servlet.http.HttpServletRequest) */ public String getFlowExecutionKey(HttpServletRequest request) { return request.getParameter(getFlowExecutionKeyParameter()); } /** * Extension point for customizing the default behaviour in FlowUrlHandler. Uses the originating URI to create a URI * for resuming the flow. * * @see org.springframework.webflow.context.servlet.FlowUrlHandler#createFlowExecutionUrl(String, String, javax.servlet.http.HttpServletRequest) */ public String createFlowExecutionUrl(String flowId, String flowExecutionKey, HttpServletRequest request) { StringBuilder url = new StringBuilder(); url.append(urlPathHelper.getOriginatingRequestUri(request)); url.append('?'); HashMap<String, String> parameters = new HashMap<String, String>(); parameters.put(getFlowExecutionKeyParameter(), flowExecutionKey); this.flowUrlHandler.appendQueryParameters(url, parameters, this.flowUrlHandler.getEncodingScheme(request)); return url.toString(); } /** * Extension point for customizing the default behaviour in FlowUrlHandler. uses the originating URI to create a URI * for starting the flow. * * @see org.springframework.webflow.context.servlet.FlowUrlHandler#createFlowDefinitionUrl(String, org.springframework.webflow.core.collection.AttributeMap, javax.servlet.http.HttpServletRequest) */ public String createFlowDefinitionUrl(String flowId, AttributeMap input, HttpServletRequest request) { StringBuilder url = new StringBuilder(); url.append(urlPathHelper.getOriginatingRequestUri(request)); if (input != null && !input.isEmpty()) { url.append('?'); this.flowUrlHandler.appendQueryParameters(url, input.asMap(), this.flowUrlHandler.getEncodingScheme(request)); } return url.toString(); } /** * Extension point for customizing the default behaviour in FlowUrlHandler. * * @see org.springframework.webflow.context.servlet.DefaultFlowUrlHandler#setEncodingScheme(String) */ public void setUrlEncodingScheme(String encodingScheme) { flowUrlHandler.setEncodingScheme(encodingScheme); } public String getFlowExecutionKeyParameter() { return this.flowExecutionKeyParameter; } public void setFlowExecutionKeyParameter(String flowExecutionKeyParameter) { this.flowExecutionKeyParameter = flowExecutionKeyParameter; } /** * Extension point for customizing the default behaviour in FlowHandler. * * @see org.springframework.webflow.mvc.servlet.FlowHandler#createExecutionInputMap(javax.servlet.http.HttpServletRequest) */ public MutableAttributeMap<Object> createExecutionInputMap(HttpServletRequest request) { return null; } /** * Extension point for customizing the default behaviour in FlowHandlerAdapter. * * @return null to fall back to default implementation * @see FlowHandlerAdapter#defaultCreateFlowExecutionInputMap(javax.servlet.http.HttpServletRequest) */ protected MutableAttributeMap<Object> defaultCreateFlowExecutionInputMap(HttpServletRequest request) { return null; } /** * Extension point for customizing the default behaviour in FlowHandler. * * @see org.springframework.webflow.mvc.servlet.FlowHandler#handleExecutionOutcome(org.springframework.webflow.execution.FlowExecutionOutcome, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) { return null; } /** * Extension point for customizing the default behaviour in FlowHandlerAdapter. * * @return false to fall back to default implementation * @see FlowHandlerAdapter#defaultHandleExecutionOutcome(String, org.springframework.webflow.execution.FlowExecutionOutcome, org.springframework.webflow.context.servlet.ServletExternalContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ protected boolean defaultHandleExecutionOutcome(String flowId, FlowExecutionOutcome outcome, ServletExternalContext context, HttpServletRequest request, HttpServletResponse response) throws IOException { return false; } /** * Extension point for customizing the default behaviour in FlowHandler. * * @see org.springframework.webflow.mvc.servlet.FlowHandler#handleException(org.springframework.webflow.core.FlowException, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ public String handleException(FlowException e, HttpServletRequest request, HttpServletResponse response) { return null; } /** * Extension point for customizing the default behaviour in FlowHandlerAdapter. * * @return false to fall back to default implementation * @see FlowHandlerAdapter#defaultHandleException(String, org.springframework.webflow.core.FlowException, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ protected boolean defaultHandleException(String flowId, FlowException e, HttpServletRequest request, HttpServletResponse response) throws IOException { return false; } /** * Allows for rewriting url prior to redirection. * * Implements support for redirecting to the currently rendered page using a placeholder. I.e: * <code> * view="externalRedirect:magnolia-redirect:main-content" * </code> * * @param url the url Spring WebFlow intends to redirect to * @return the rewritten url or if no change was made returns the passed in url unchanged */ protected String rewriteRedirectUrl(String url) throws IOException { try { if (url.equals("/" + UuidRedirectViewResolver.REDIRECT_MAIN_CONTENT_PLACEHOLDER)) { Node node = MgnlContext.getAggregationState().getMainContentNode(); String workspaceName = node.getSession().getWorkspace().getName(); String identifier = node.getIdentifier(); url = LinkUtil.convertUUIDtoURI(identifier, workspaceName); } return url; } catch (RepositoryException e) { throw new IOException("Could not convert placeholder to link", e); } catch (LinkException e) { throw new IOException("Could not convert placeholder to link", e); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { super.setApplicationContext(applicationContext); super.getFlowHandlerAdapter().setApplicationContext(applicationContext); } @Override public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); super.getFlowHandlerAdapter().afterPropertiesSet(); } /** * Custom FlowUrlHandler that allows subclasses of AbstractSingleFlowController to customize behaviour. */ protected class CustomFlowUrlHandler extends DefaultFlowUrlHandler { public String getFlowId(HttpServletRequest request) { return AbstractSingleFlowController.this.getFlowId(request); } @Override public String getFlowExecutionKey(HttpServletRequest request) { return AbstractSingleFlowController.this.getFlowExecutionKey(request); } @Override public String createFlowExecutionUrl(String flowId, String flowExecutionKey, HttpServletRequest request) { return AbstractSingleFlowController.this.createFlowExecutionUrl(flowId, flowExecutionKey, request); } @Override public String createFlowDefinitionUrl(String flowId, AttributeMap input, HttpServletRequest request) { return AbstractSingleFlowController.this.createFlowDefinitionUrl(flowId, input, request); } /** * Overridden to allow access. */ @Override public <T> void appendQueryParameters(StringBuilder url, Map<java.lang.String, T> parameters, String encodingScheme) { super.appendQueryParameters(url, parameters, encodingScheme); } /** * Overridden to allow access. */ @Override public String getEncodingScheme(HttpServletRequest request) { return super.getEncodingScheme(request); } } /** * Custom FlowHandler that handles our single flow. Allows subclasses of AbstractSingleFlowController to customize * behaviour. */ protected class CustomFlowHandler extends AbstractFlowHandler { public String getFlowId() { return flowId; } @Override public MutableAttributeMap<Object> createExecutionInputMap(HttpServletRequest request) { return AbstractSingleFlowController.this.createExecutionInputMap(request); } @Override public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) { return AbstractSingleFlowController.this.handleExecutionOutcome(outcome, request, response); } @Override public String handleException(FlowException e, HttpServletRequest request, HttpServletResponse response) { return AbstractSingleFlowController.this.handleException(e, request, response); } } /** * Custom FlowHandlerAdapter that allows subclasses of AbstractSingleFlowController to customize default behaviour. */ protected class CustomFlowHandlerAdapter extends FlowHandlerAdapter { @Override protected MutableAttributeMap<Object> defaultCreateFlowExecutionInputMap(HttpServletRequest request) { MutableAttributeMap<Object> map = AbstractSingleFlowController.this.defaultCreateFlowExecutionInputMap(request); return map != null ? map : super.defaultCreateFlowExecutionInputMap(request); } @Override protected void defaultHandleExecutionOutcome(String flowId, FlowExecutionOutcome outcome, ServletExternalContext context, HttpServletRequest request, HttpServletResponse response) throws IOException { if (!AbstractSingleFlowController.this.defaultHandleExecutionOutcome(flowId, outcome, context, request, response)) { super.defaultHandleExecutionOutcome(flowId, outcome, context, request, response); } } @Override protected void defaultHandleException(String flowId, FlowException e, HttpServletRequest request, HttpServletResponse response) throws IOException { if (!AbstractSingleFlowController.this.defaultHandleException(flowId, e, request, response)) { super.defaultHandleException(flowId, e, request, response); } } @Override protected void sendRedirect(String url, HttpServletRequest request, HttpServletResponse response) throws IOException { url = rewriteRedirectUrl(url); super.sendRedirect(url, request, response); } } }